/*	--- 异常测试 --- 

在Java程序中，异常处理是非常重要的。

我们自己编写的方法，也经常抛出各种异常。对于可能抛出的异常进行测试，本身就是测试的重要环节。

因此，在编写JUnit测试的时候，除了正常的输入输出，我们还要特别针对可能导致异常的情况进行测试。

我们仍然用Factorial举例：	*/
public class Factorial {
	public static long fact(long n) {
		if (n < 0) {
			throw new IllegalArgumentException();
		}

		long r = 1;

		for (long i = 1; i <= n; i++) {
			r = r * i;
		}

		return r;
	}
}


/*
在方法入口，我们增加了对参数n的检查，如果为负数，则直接抛出IllegalArgumentException。

现在，我们希望对异常进行测试。
在JUnit测试中，我们可以编写一个@Test方法专门测试异常：	*/
@Test
void testNegative() {
	assertThrows(IllegalArgumentException.class, new Executable() {
		@Override
		public void execute() throws Throwable {
			Factorial.fact(-1);
		}
	});
}


/*
JUnit提供assertThrows()来期望捕获一个指定的异常。第二个参数Executable封装了我们要执行的会产生异常的代码。当我们执行Factorial.fact(-1)时，必定抛出IllegalArgumentException。assertThrows()在捕获到指定异常时表示通过测试，未捕获到异常，或者捕获到的异常类型不对，均表示测试失败。


有些童鞋会觉得编写一个Executable的匿名类太繁琐了。实际上，Java 8开始引入了函数式编程，所有单方法接口都可以简写如下：	*/
@Test
void testNegative() {
	assertThrows(IllegalArgumentException.class, () -> {
		Factorial.fact(-1);
	});
}

/*
上述奇怪的->语法就是函数式接口的实现代码，我们会在后面详细介绍。现在，我们只需要通过这种固定的代码编写能抛出异常的语句即可。
*/



/*
练习
观察Factorial.fact()方法，注意到由于long型整数有范围限制，当我们传入参数21时，得到的结果是-4249290049419214848，而不是期望的51090942171709440000，因此，当传入参数大于20时，Factorial.fact()方法应当抛出ArithmeticException。请编写测试并修改实现代码，确保测试通过。

*/

//src:
public class Factorial {
    public static long fact(long n) {
        if (n < 0) {
            throw new IllegalArgumentException();
        } else if (n > 20) {
            throw new ArithmeticException();
        }

        long r = 1;

        for (long i = 1; i <= n; i++) {
            r = r * i;
        }
        
        return r;
    }
}


//test:
public class FactorialTest {
    @Test
    void testFact() {
        assertEquals(1, Factorial.fact(1));
        assertEquals(2, Factorial.fact(2));
        assertEquals(6, Factorial.fact(3));
        assertEquals(3628800, Factorial.fact(10));
        assertEquals(2432902008176640000L, Factorial.fact(20));
    }

    @Test
    void testNegative() {
        assertThrows(IllegalArgumentException.class, () -> {
            Factorial.fact(-1);
        });
    }

    @Test
    void testBound() {
        assertThrows(ArithmeticException.class, () -> {
            Factorial.fact(21);
        });
    }
}

/* --- 异常测试 の 小结 --- 

1.测试异常可以使用assertThrows()，期待捕获到指定类型的异常；

2.对可能发生的每种类型的异常都必须进行测试。


*/

























